package gbench.sandbox.data.index;

import static gbench.common.matlib.algebra.lisp.MyRecord.REC;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;

import gbench.common.matlib.algebra.lisp.IRecord;

/**
 * 
 * 数据按照 3 级索引进行组织 <br>
 * algorithm->table->indexEngine 即(uuid->item) <br>
 * 可以理解 理解为 这是一个 algorithm x table 的矩阵 <br>
 * 
 * algorithm 是 registry 的类型 <br>
 * registry 是 (key,index) 的 结构 <br>
 * index 是 globalIndex 的数据索引 <br>
 * 
 * @author Administrator
 *
 */
public abstract class AbstractTableSearchEngine implements ITableSearchEngine {

    /**
     * 添加 算法
     * 
     * @param algorithm 算法维度
     * @return TableAlgorithm
     */
    public TableAlgorithm addAlgorithm(final Algorithm algorithm) {
        return tableAlgoritms.computeIfAbsent(algorithm, k -> new TableAlgorithm(algorithm, this));
    }

    /**
     * 
     * @param uuid
     * @param dataItem
     */
    @Override
    public UUID globalPut(final UUID uuid, final IRecord dataItem) {
        final var guid = uuid == null ? UUID.randomUUID() : uuid;
        globalIndex.put(guid, dataItem);
        return guid;
    }

    /**
     * 
     * @param uuid
     * @return
     */
    @Override
    public IRecord globalGet(final UUID uuid) {
        return this.globalIndex.get(uuid);
    }

    /**
     * 
     * @author Administrator
     *
     */
    public class SearchSession {

        /**
         * 
         * @param key_tbl_bindings 键名 数据表 序列
         */
        @SuppressWarnings("unchecked")
        public SearchSession(final Algorithm algorithm, final String... key_tbl_bindings) {
            this.keyTableBindings = (Map<String, String>) (Object) REC((Object[]) key_tbl_bindings).toMap();
            this.algorithm = algorithm;
        }

        /**
         * 获取注册表
         * 
         * @param algorithm 注册名
         * @param table     表名
         * @return 获取索引引擎
         */
        public IndexEngine getIndexEngine(final Algorithm algorithm, final String table) {
            final var tableAlgorithm = AbstractTableSearchEngine.this.tableAlgoritms.get(algorithm);
            return tableAlgorithm.getEngine(table);
        }

        /**
         * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
         * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
         * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
         * 
         * @param keywords 关键词集合 {(key:keyword)} 每个关键词,用 key 标识,<br>
         *                 而key由绑定了数据table,所以就可以 key+index 就对应了 就提的 表的 <br>
         *                 索引。(key:keyword) 集合就定义了一个 用keyword 在 key包绑定的表 keytable 的 <br>
         *                 index_name 进行 检索的关系。 <br>
         * @return
         */
        public IRecord encode(final IRecord keywords) {
            return this.findOne(Algorithm.KEY_VALUE, keywords, "name", e -> e.str("id"));
        }

        /**
         * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
         * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
         * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
         * 
         * @param keywords   关键词集合 {(key:keyword)} 每个关键词,用 key 标识,<br>
         *                   而key由绑定了数据table,所以就可以 key+index 就对应了 就提的 表的 <br>
         *                   索引。(key:keyword) 集合就定义了一个 用keyword 在 key包绑定的表 keytable 的
         *                   <br>
         *                   index_name 进行 检索的关系。 <br>
         * @param hit_mapper 值变换函数,命中数据即hit的变换函数
         * @return
         */
        public IRecord encode(final IRecord keywords, final Function<IRecord, Object> hit_mapper) {
            return this.findOne(Algorithm.KEY_VALUE, keywords, "name", hit_mapper);
        }

        /**
         * 
         * @param keywords 关键词集合 {(key:keyword)} 每个关键词,用 key 标识,<br>
         *                 而key由绑定了数据table,所以就可以 key+index 就对应了 就提的 表的 <br>
         *                 索引。(key:keyword) 集合就定义了一个 用keyword 在 key包绑定的表 keytable 的 <br>
         *                 index_name 进行 检索的关系。 <br>
         * @return
         */
        public IRecord decode(final IRecord keywords) {
            return this.findOne(Algorithm.KEY_VALUE, keywords, "id", e -> e.str("name"));
        }

        /**
         * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
         * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
         * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
         * 
         * @param keywords 关键词集合 {(key:keyword)} 每个关键词,用 key 标识,<br>
         *                 而key由绑定了数据table,所以就可以 key+index 就对应了 就提的 表的 <br>
         *                 索引。(key:keyword) 集合就定义了一个 用keyword 在 key包绑定的表 keytable 的 <br>
         *                 index_name 进行 检索的关系。 <br>
         * @param mapper   值变换函数
         * @return
         */
        public IRecord decode(final IRecord keywords, final Function<IRecord, Object> mapper) {
            return this.findOne(Algorithm.KEY_VALUE, keywords, "id", mapper);
        }

        /**
         * 数据解析
         * 
         * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
         * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
         * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
         * 
         * @param keywords   关键词集合 {(key:keyword)} 每个关键词,用 key 标识,<br>
         *                   而key由绑定了数据table,所以就可以 key+index 就对应了 就提的 表的 <br>
         *                   索引。(key:keyword) 集合就定义了一个 用keyword 在 key包绑定的表 keytable 的
         *                   index_name 进行 检索的关系。 <br>
         * @param index_name 索引名
         * @param hit_mapper 值变换函数,命中数据即hit的变换函数
         * @return 解析后的数据
         */
        public IRecord findOne(final IRecord keywords, final String index_name, Function<IRecord, Object> hit_mapper) {

            return this.findOne(this.algorithm, keywords, index_name, hit_mapper);
        }

        /**
         * 列名解析
         * 
         * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
         * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
         * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
         * 
         * @param algorithm  索引算法
         * @param keywords   关键词集合 {(key:keyword)} 每个关键词,用 key 标识,<br>
         *                   而key由绑定了数据table,所以就可以 key+index 就对应了 就提的 表的 <br>
         *                   索引。(key:keyword) 集合就定义了一个 用keyword 在 key包绑定的表 keytable 的
         *                   <br>
         *                   index_name 进行 检索的关系。 <br>
         * @param index_name 索引名
         * @param hit_mapper 值变换函数,命中数据即hit的变换函数
         * @return 解析后的数据
         */
        public IRecord findOne(final Algorithm algorithm, final IRecord keywords, final String index_name,
                Function<IRecord, Object> hit_mapper) {
            final var result = REC(); // 返回结果

            keywords.forEach(tuple -> { // 键名键值
                final var key = tuple._1();
                final var keyword = tuple._2; // 关键词
                final var table = this.getTableByKey(key); // 数据表
                final var indexEngine = this.getIndexEngine(algorithm, table); // 提取索引疫情

                if (table != null && indexEngine != null) {
                    final var entity = indexEngine.findOne(index_name, keyword); // 使用keyword在index_name中检索数据
                    final var value = entity == null ? null : hit_mapper.apply(entity);
                    result.add(key, value); // 提取数据索引
                } else {
                    result.add(key, tuple._2); // 直接返回值
                } // if
            }); // forEach

            return result;
        }

        /**
         * 获取 key 所编订的 表名
         * 
         * @param key keywords IRecord 的 key名
         * @return 表名
         */
        public String getTableByKey(final String key) {
            return this.keyTableBindings.get(key);
        }

        /**
         * 
         * @param action
         */
        public <T> T withTransaction(final Function<SearchSession, T> action) {
            return action.apply(this);
        }

        /**
         * 
         * @return
         */
        public Map<String, String> getKeyTableBindings() {
            return keyTableBindings;
        }

        private final Algorithm algorithm;
        private final Map<String, String> keyTableBindings; /// 键名 表名的 关系绑定
    }

    /**
     * 
     * @return
     */
    public IRecord getConfig() {
        return config;
    }

    /**
     * 设置 配置信息
     * 
     * @param config 配置信息 比如数据库 url,user,password 等字段序列
     * @return
     */
    public IRecord setConfig(IRecord config) {
        return this.config.add(config);
    }

    /**
     * 
     */
    private final Map<Algorithm, TableAlgorithm> tableAlgoritms = new HashMap<>();
    private final GlobalIndex<UUID, IRecord> globalIndex = new GlobalIndex<>(); // 全局数据索引
    private final IRecord config = REC(); // 全局数据索引

}
